/*
 * Camera.cpp
 *
 *  Created on: Oct 16, 2012
 *      Author: travis
 */

#include "Camera.h"

namespace Camera
{

glm::vec3 CamPos(0.0);

Camera::Camera(Timer *_fps)
{
	terrain = NULL;

	zoom = Engine::cameraSettings.MaxZoom; //7
	fps = _fps;
	time = 0.0;

	STEP = 15; //1.5
	zoomspeed = 0.0;
	xspeed = 0.0;
	yspeed = 0.0;

	az = ax = ay = 0;

	origin = glm::vec3(0.0);
	camPos = glm::vec3(0.0);

	waterHigh = 0.0;
	oldHigh = 0.0;

	angle = 45.0;

	setupView();
}

Camera::~Camera()
{
}

void Camera::control(SDL_Event *event)
{
	switch (event->type)
	{
	case SDL_MOUSEMOTION:
		break;

	case SDL_MOUSEBUTTONDOWN:
		//printf("Mouse button %d pressed at (%d,%d)\n",
		//		event->button.button, event->button.x, event->button.y);

		if ((event->button.button == 5) && (zoom < Engine::cameraSettings.MaxZoom))
			az = Engine::cameraSettings.MaxStep;

		if ((event->button.button == 4) && (zoom > Engine::cameraSettings.MinZoom))
			az = -Engine::cameraSettings.MaxStep;
		break;
	}

}

void Camera::lookAt(glm::vec2 pos, float _z)
{
	zoom = _z;
	lookAt(pos);
}

void Camera::lookAt(glm::vec2 pos)
{
	origin.x = pos.x;
	origin.z = pos.y;

	// stop camera move
	ax = ay = az = 0;
	setupView();
}

void Camera::setupView()
{
	// check if terrain is set
	// set high of origin
	if (terrain != NULL)
	{
		float y = terrain->getInterYVal(glm::vec2(origin.x, origin.z));

		if (y < waterHigh)
			origin.y = waterHigh;
		else if (oldHigh > 0.0)
			origin.y = (oldHigh + y) / 2.0;
		else
			origin.y = y;

	}

	// set camera position
	camPos.x = origin.x - zoom * cos(angle * PI / 180.0);
	camPos.y = origin.y + zoom * sin(angle * PI / 180.0);
	camPos.z = origin.z;

	oldHigh = origin.y;

	// setup custom matrices
	glm::mat4 View = glm::lookAt(camPos, // Camera in World Space
			origin, // and looks at the origin
			glm::vec3(0.0, 1.0, 0.0) // Head is up (set to 0,-1,0 to look upside-down)
					);

	// send new view matrix to glMatrix class
	World::world->setView(View);

	//update frustum
	World::world->extractFrustum();

	// update octree visibility
	Octree::updateScene();

	CamPos = camPos;
}

void Camera::update()
{
	int mouse_x, mouse_y;
	SDL_GetMouseState(&mouse_x, &mouse_y);

	time = (float) fps->getDeltaClock();

	if ((mouse_y < Engine::cameraSettings.EdgeSens) && (mouse_y >= 0)
			&& (origin.x < ((terrain->get_size() - 2) * terrain->get_step())))
		ay = Engine::cameraSettings.MaxStep;

	if ((mouse_y
			> (Engine::screenSettings.screenHeight - Engine::cameraSettings.EdgeSens))
			&& (origin.x > 0.0))
		ay = -Engine::cameraSettings.MaxStep;

	if ((mouse_x < Engine::cameraSettings.EdgeSens) && (origin.z > 0.0))
		ax = -Engine::cameraSettings.MaxStep;

	if ((mouse_x
			> (Engine::screenSettings.screenWidth - Engine::cameraSettings.EdgeSens))
			&& (origin.z < ((terrain->get_size() - 2) * terrain->get_step())))
		ax = Engine::cameraSettings.MaxStep;

	// setup speed
	zoomspeed = (float) az / 100;
	xspeed = (float) ax / 100;
	yspeed = (float) ay / 100;

	// move
	zoom = zoom + zoomspeed * 2 * time;
	origin.z = origin.z + xspeed * time;
	origin.x = origin.x + yspeed * time;

	// update only when camera move
	if ((ax != 0) || (ay != 0) || (az != 0))
		setupView();

	// slow down move
	if (ax > 0)
		ax -= Engine::cameraSettings.Deceleration;
	if (ax < 0)
		ax += Engine::cameraSettings.Deceleration;

	if (ay > 0)
		ay -= Engine::cameraSettings.Deceleration;
	if (ay < 0)
		ay += Engine::cameraSettings.Deceleration;

	if (az > 0)
		az -= Engine::cameraSettings.Deceleration;
	if (az < 0)
		az += Engine::cameraSettings.Deceleration;
	//-------------------------
}

float Camera::getZoom()
{
	return zoom;
}

Terrain::Terrain*& Camera::getTerrain()
{
	return terrain;
}

void Camera::setTerrain(Terrain::Terrain*& terrain)
{
	this->terrain = terrain;
}

float Camera::getWaterHigh() const
{
	return waterHigh;
}

void Camera::setWaterHigh(float waterHigh)
{
	this->waterHigh = waterHigh;
}

const glm::vec3& Camera::getCamPos() const
{
	return camPos;
}

void Camera::setCamPos(const glm::vec3& camPos)
{
	this->camPos = camPos;
}

const glm::vec3& Camera::getOrigin() const
{
	return origin;
}

void Camera::setOrigin(const glm::vec3& origin)
{
	this->origin = origin;
}

float Camera::getAngle() const
{
	return angle;
}

void Camera::setAngle(float angle)
{
	this->angle = angle;
}

Camera *camera;

void initCamera(Timer *fps)
{
	camera = new Camera(fps);
}

void deleteCamera()
{
	delete (camera);
}

}
